﻿using Microsoft.EntityFrameworkCore;
using MiniFox;
using MiniFox.Components;
using MiniFox.Data.EntityFramework;
using MiniFox.Infrastructure;
using MiniFox.Platform;
using MiniFox.Platform.Models;
using MiniFox.Platform.OAuth2;
using MiniFox.Platform.OAuth2.Models;
using MiniFox.Platform.OAuth2.Providers;
using MiniFox.Utility;

namespace OAuth2.Data
{
    /// <summary>
    /// 通行证数据提供者
    /// </summary>
    [AutoResolve]
    public class PassportProvider : BaseOAuth2DataProvider, IPassportProvider
    {
        /// <summary>
        /// 
        /// </summary>
        public PassportProvider()
        {
        }
        /// <summary>
        /// 账户激活
        /// </summary>
        /// <param name="ticket">关联的激活票据</param>
        /// <returns></returns>
        public void ActivateIdentity(OpenAuthIdentity openAuthIdentity)
        {
            SysUser sys_user = this.CurrentDbContext.SysUser.SingleOrDefault(u => u.UserId == openAuthIdentity.GlobalID);
            if (sys_user == null)
            {
                return;
            }
            sys_user.UserStatus = IdentityStatus.Standard.ToString();
            this.Update(sys_user);
        }

        public OpenAuthIdentity CreateIdentity(string loginName, string secEmail, string cellphone, string password)
        {
            SysUser sys_user = new SysUser();
            string id = SequenceGUID.NewGUID();
            sys_user.UserId = id;
            sys_user.LoginName = loginName;
            sys_user.SecEmail = secEmail;
            sys_user.Cellphone = cellphone;
            sys_user.CreateTime = DateTime.Now;
            sys_user.UserStatus = IdentityStatus.Unverified.ToString();

            SysPassword sys_Password = new SysPassword
            {
                UserId = id,
                Password = password
            };

            if (this.Insert(sys_user, sys_Password) <= 0)
                return null;

            return MappingIdentity(sys_user);
        }

        public OpenAuthIdentity CreateIdentityWithLoginName(string loginName, string password)
        {
            SysUser sys_user = new SysUser();
            string id = SequenceGUID.NewGUID();
            sys_user.UserId = id;
            sys_user.LoginName = loginName;
            sys_user.CreateTime = DateTime.Now;
            sys_user.UserStatus = IdentityStatus.Unverified.ToString();

            SysPassword sys_Password = new SysPassword
            {
                UserId = id,
                Password = password
            };
            if (this.Insert(sys_user, sys_Password) <= 0)
                return null;

            return MappingIdentity(sys_user);
        }

        public OpenAuthIdentity CreateIdentityWithSecEmail(string secEmail, string password)
        {
            SysUser sys_user = new SysUser();
            string id = SequenceGUID.NewGUID();
            sys_user.UserId = id;
            sys_user.SecEmail = secEmail;
            sys_user.CreateTime = DateTime.Now;
            sys_user.UserStatus = IdentityStatus.Unverified.ToString();

            SysPassword sys_Password = new SysPassword
            {
                UserId = id,
                Password = password
            };
            if (this.Insert(sys_user, sys_Password) <= 0)
                return null;

            return MappingIdentity(sys_user);
        }

        public OpenAuthIdentity CreateIdentityWithCellphone(string cellphone, string password)
        {
            SysUser sys_user = new SysUser();
            string id = SequenceGUID.NewGUID();
            sys_user.UserId = id;
            sys_user.SecEmail = cellphone;
            sys_user.CreateTime = DateTime.Now;
            sys_user.UserStatus = IdentityStatus.Unverified.ToString();

            SysPassword sys_Password = new SysPassword
            {
                UserId = id,
                Password = password
            };
            if (this.Insert(sys_user, sys_Password) <= 0)
                return null;

            return MappingIdentity(sys_user);
        }

        public void DisablePassportToken(OpenAuthIdentity identity, ClientInfomation clientInfomation)
        {
            SysPassportToken[] sys_tokens = this.CurrentDbContext.SysPassportToken.Where(t => t.UserId == identity.GlobalID).ToArray();
            if (sys_tokens.Length > 0)
            {
                foreach (SysPassportToken token in sys_tokens)
                {
                    this.Delete(token);
                    try
                    {
                        SysSignLog sys_signInLog = this.CurrentDbContext.SysSignLog.SingleOrDefault(u => u.TicketId == token.TicketId);
                        sys_signInLog.SignOffTime = DateTime.Now;
                        this.Update(sys_signInLog);
                    }
                    catch
                    {

                    }
                }
            }
        }

        public AuthorizeResult GetDeviceAuthorize(Ticket authTicket)
        {
            var deviceAuth = new AuthorizeResult(authTicket);
            var auth = this.CurrentDbContext.SysDeviceAuth.SingleOrDefault(t => t.TicketId == authTicket.TicketID);
            if (auth == null)
            {
                deviceAuth.Status = DeviceAuthorizeStatus.Default;
            }
            else
            {
                deviceAuth.Status = auth.Authorized ? DeviceAuthorizeStatus.Authorized : DeviceAuthorizeStatus.Verified;

                var openAuthUser = this.CurrentDbContext.SysOpenAuthUser.SingleOrDefault(u => u.OpenId == auth.OpenId && u.AuthName == auth.AuthName);
                deviceAuth.OpenAuthUser = new ExternalOpenAuthUser(openAuthUser.OpenId, openAuthUser.AuthName)
                {
                    UserData = openAuthUser.UserData.ToJsonObject()
                };
            }
            return deviceAuth;
        }

        public OpenAuthIdentity GetIdentity(string signName, string password)
        {
            SysUser sys_user = this.CurrentDbContext.SysUser.SingleOrDefault(i => i.UserId == signName || i.LoginName == signName || i.SecEmail == signName || i.Cellphone == signName);
            if (!this.CurrentDbContext.SysPassword.Any(p => p.UserId == sys_user.UserId && p.Password == password))
                return null;
            return MappingIdentity(sys_user);
        }

        public OpenAuthIdentity GetIdentity(string signName)
        {
            var sys_user = this.CurrentDbContext.SysUser.SingleOrDefault(i => i.Cellphone == signName);
            return MappingIdentity(sys_user);
        }

        public OpenAuthIdentity GetIdentityByEmail(string secEmail)
        {
            var sys_user = this.CurrentDbContext.SysUser.SingleOrDefault(i => i.SecEmail == secEmail);
            return MappingIdentity(sys_user);
        }

        public OpenAuthIdentity GetIdentityByCellphone(string cellphone)
        {
            var sys_user = this.CurrentDbContext.SysUser.SingleOrDefault(i => i.Cellphone == cellphone);
            return MappingIdentity(sys_user);
        }

        public OpenAuthIdentity GetIdentity(PassportToken passportToken)
        {
            if (passportToken == null)
                return null;
            SysPassportToken sys_token = this.CurrentDbContext.SysPassportToken.SingleOrDefault(t => t.TicketId == passportToken.TicketID);
            if (sys_token == null)
            {
                return null;
            }
            var sys_user = this.CurrentDbContext.SysUser.SingleOrDefault(i => i.UserId == sys_token.UserId);
            OpenAuthIdentity identity = MappingIdentity(sys_user);
            if (identity == null)
            {
                this.Delete(sys_token);
                return null;
            }
            return identity;
        }

        protected virtual void CreateAuthUser(ExternalOpenAuthUser openAuthUser)
        {
            var sys_openauthuser = new SysOpenAuthUser();
            sys_openauthuser.OpenId = openAuthUser.OpenID;
            sys_openauthuser.AuthName = openAuthUser.AuthName;
            sys_openauthuser.UserData = openAuthUser.UserData.ToJsonString();
            sys_openauthuser.CreateTime = DateTime.Now;
            sys_openauthuser.UserId = null;
            this.Insert(sys_openauthuser);
        }

        public OpenAuthIdentity GetIdentity(ExternalOpenAuthUser openAuthUser)
        {
            SysOpenAuthUser sys_openauthuser = this.CurrentDbContext.SysOpenAuthUser.SingleOrDefault(u => u.AuthName == openAuthUser.AuthName && u.OpenId == openAuthUser.OpenID);

            SysUser sys_user = this.CurrentDbContext.SysUser.SingleOrDefault(u => u.UserId == sys_openauthuser.UserId);
            return MappingIdentity(sys_user);
        }

        public void SaveOpenAuthUser(ExternalOpenAuthUser openAuthUser)
        {
            var sys_openauthuser = this.CurrentDbContext.SysOpenAuthUser.SingleOrDefault(u => u.AuthName == openAuthUser.AuthName && u.OpenId == openAuthUser.OpenID);
            if (sys_openauthuser != null)
            {
                sys_openauthuser.UserData = openAuthUser.UserData.ToJsonString();
                this.Update(sys_openauthuser);
            }
            else
            {
                this.CreateAuthUser(openAuthUser);
            }
        }

        public void SavePassportToken(OpenAuthIdentity identity, PassportToken passportToken, ClientInfomation clientInfomation)
        {
            string clientPlatform = clientInfomation.Platform;
            string browser = clientInfomation.Browser;
            string device = clientInfomation.Device;
            //var exists = this.CurrentDbContext.SysPassportToken.Any(t => t.TicketId == passportToken.TicketID);
            //if (exists)
            //{
            //    return;
            //}
            SysPassportToken sys_token = new SysPassportToken();
            sys_token.TicketId = passportToken.TicketID;
            sys_token.UserId = identity.GlobalID;
            sys_token.CreateTime = passportToken.CreationTime.DateTime;
            sys_token.RenewTime = passportToken.RenewTime.DateTime;
            sys_token.Duration = passportToken.Duration;
            sys_token.ExpireTime = passportToken.ExpiredTime.DateTime;
            sys_token.ClientIp = clientInfomation.ClientIP;
            sys_token.UserAgent = clientInfomation.UserAgent;

            if (this.Insert(sys_token) <= 0)
                return;
            try
            {
                SysSignLog sys_signInLog = new SysSignLog();
                sys_signInLog.TicketId = passportToken.TicketID;
                sys_signInLog.SignOnTime = passportToken.CreationTime.DateTime;
                sys_signInLog.UserId = identity.GlobalID;
                sys_signInLog.NickName = identity.DisplayName;
                sys_signInLog.LoginName = identity.Name;
                sys_signInLog.SecEmail = identity.SecEmail;
                sys_signInLog.Cellphone = identity.Cellphone;
                sys_signInLog.IpAddress = clientInfomation.ClientIP;

                sys_signInLog.Platform = clientPlatform;
                sys_signInLog.Browser = browser;
                sys_signInLog.Device = device;
                this.Insert(sys_signInLog);
            }
            catch
            {

            }
        }

        public bool IsUnusedCellphone(string cellphone)
        {
            return !this.CurrentDbContext.SysUser.Any(i => i.Cellphone == cellphone);
        }

        public bool IsUnusedLoginName(string loginName)
        {
            return !this.CurrentDbContext.SysUser.Any(i => i.LoginName == loginName);
        }

        public bool IsUnusedSecEmail(string secEmail)
        {
            return !this.CurrentDbContext.SysUser.Any(i => i.SecEmail == secEmail);
        }

        public bool PinCodeValidation(Ticket authTicket, string cellphone, string pinCode)
        {
            return this.CurrentDbContext.SysPincode.Any(t => t.TicketId == authTicket.TicketID && t.SignName == cellphone && t.Code == pinCode);
        }

        public bool ResetPassword(Ticket ticket, string password)
        {
            SysTicket sys_ticket = this.CurrentDbContext.SysTicket.SingleOrDefault(t => t.TicketId == ticket.TicketID && !t.IsVerified && t.TicketType == TicketType.RetakePassword.ToString());
            if (sys_ticket == null)
                return false;
            var sys_password = this.CurrentDbContext.SysPassword.SingleOrDefault(p => p.UserId == sys_ticket.Data);
            if (sys_password == null)
            {
                this.Delete(sys_ticket);
                return false;
            }

            sys_ticket.IsVerified = true;
            sys_password.Password = password;

            List<BatchOperationItem> list = new()
            {
                new BatchOperationItem { Entity = sys_ticket, State = EntityState.Modified },
                new BatchOperationItem { Entity = sys_password, State = EntityState.Modified }
            };

            return this.CommitBatch(list.ToArray()) <= 0;
        }

        public bool SetOpenAuthUser(Ticket authTicket, ExternalOpenAuthUser openAuthUser)
        {
            if (!this.CurrentDbContext.SysOpenAuthUser.Any(u => u.OpenId == openAuthUser.OpenID && u.AuthName == openAuthUser.AuthName))
            {
                CreateAuthUser(openAuthUser);
            }
            var ticket = new SysDeviceAuth();
            ticket.TicketId = authTicket.TicketID;
            ticket.OpenId = openAuthUser.OpenID;
            ticket.AuthName = openAuthUser.AuthName;
            ticket.CreateTime = authTicket.CreationTime.DateTime;
            ticket.ExpireTime = authTicket.ExpiredTime.DateTime;
            ticket.Authorized = false;
            return this.Insert(ticket) > 0;
        }

        public void SetDeviceAuthorized(Ticket authTicket)
        {
            var auth = this.CurrentDbContext.SysDeviceAuth.SingleOrDefault(t => t.TicketId == authTicket.TicketID);
            auth.Authorized = true;
            this.Update(auth);
        }

        public void BindIdentity(ExternalOpenAuthUser openAuthUser, OpenAuthIdentity identity)
        {
            var ouser = this.CurrentDbContext.SysOpenAuthUser.SingleOrDefault(u => u.OpenId == openAuthUser.OpenID && u.AuthName == openAuthUser.AuthName);
            if (ouser != null)
            {
                ouser.UserId = identity.GlobalID;
                this.Update(ouser);
            }
        }
    }
}
